/*
 *  JSShell.c
 *  jscsh
 *
 *  Created by Jonathan Hohle on 1/8/08.
 *  Copyright 2008 hohle.net. All rights reserved.
 *
 */

#include "JSShell.h"
#import <Cocoa/Cocoa.h>
#include <stdio.h>
#include <getopt.h>
#include "jscsh.h"
#include "util.h"

void JSShell_extendHost(JSContextRef context, JSObjectRef contextGlobalObject)
{
    JSStaticFunction staticFunctions[] = {
        {"print", JSShell_print, kJSPropertyAttributeNone},
        {"readline", JSShell_readline, kJSPropertyAttributeNone},
        {"gc", JSShell_gc, kJSPropertyAttributeNone},
        {"quit", JSShell_quit, kJSPropertyAttributeNone},
        {"system", JSShell_system, kJSPropertyAttributeNone},
        {"load", JSShell_load, kJSPropertyAttributeNone},
        {"help", JSShell_help, kJSPropertyAttributeNone},
        {"pread", JSShell_pread, kJSPropertyAttributeNone},
        {0, 0, 0}
    };
    
    JSClassDefinition JSSHell_definition = {
        0,
        kJSClassAttributeNone,
        
        "JSShell",
        NULL,
        
        NULL,
        staticFunctions,
        
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL
    };
        
    //
    // create a JSShell Object:
    //   var JSShell = {
    //     print:    function () { [native code] },
    //     readline: function () { [native code] },
    //     gc:       function () { [native code] },
    //     quit:     function () { [native code] },
    //     system:   function () { [native code] },
    //     load:     function () { [native code] },
    //     help:     function () { [native code] },
    //     pread:    function () { [native code] }
    //  };
    //
    JSClassRef jsshell = JSClassCreate(&JSSHell_definition);
    JSObjectRef jsshellObject = JSObjectMake(context, jsshell, NULL);
    JSStringRef jsshellString = JSStringCreateWithUTF8CString("JSShell");
    JSObjectSetProperty(context,
                        contextGlobalObject,
                        jsshellString,
                        jsshellObject,
                        kJSPropertyAttributeNone,
                        NULL);
    JSClassRelease(jsshell);
    JSStringRelease(jsshellString);
    
    //
    // Add the same functions in the global scope
    //
    JSStaticFunction* staticFunction = staticFunctions;
    
    while (staticFunction->name && staticFunction->callAsFunction)
    {
        createGlobalJSFunction(staticFunction->name,
                               context,
                               contextGlobalObject,
                               staticFunction->callAsFunction);
        ++staticFunction;
    }
}

JSValueRef JSShell_print(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    /* UNUSED_PARAM(object);
     UNUSED_PARAM(thisObject); */
    
    if (argumentCount > 0)
    {
        JSStringRef string = JSValueToStringCopy(context, arguments[0], exception);
        size_t numChars = JSStringGetMaximumUTF8CStringSize(string);
        char stringUTF8[numChars];
        JSStringGetUTF8CString(string, stringUTF8, numChars);
        printf("%s\n", stringUTF8);
        JSStringRelease(string);
    }
    
    return JSValueMakeUndefined(context);
}

JSValueRef JSShell_readline(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    /* UNUSED_PARAM(object);
     UNUSED_PARAM(thisObject); */
    
    char line[4000];
    fgets(line, 4000, stdin);
    JSStringRef stringRef = JSStringCreateWithUTF8CString(line);
    
    JSValueRef returnValue = JSValueMakeString(context, stringRef);
    JSStringRelease(stringRef);
    
    return returnValue;
}


JSValueRef JSShell_gc(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    JSGarbageCollect(context);   
    return JSValueMakeUndefined(context);
}

JSValueRef JSShell_quit(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    exit(0);
}

JSValueRef JSShell_system(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount > 0)
    {
        JSStringRef string = JSValueToStringCopy(context, arguments[0], exception);
        size_t numChars = JSStringGetMaximumUTF8CStringSize(string);
        char stringUTF8[numChars];
        JSStringGetUTF8CString(string, stringUTF8, numChars);
        
        return JSValueMakeNumber(context,
                                 system(stringUTF8));
    }
    return JSValueMakeUndefined(context);
}


JSValueRef JSShell_pread(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount > 0)
    {
        JSStringRef string = JSValueToStringCopy(context, arguments[0], exception);
        size_t numChars = JSStringGetMaximumUTF8CStringSize(string);
        char stringUTF8[numChars];
        JSStringGetUTF8CString(string, stringUTF8, numChars);
        
        FILE* output = popen(stringUTF8, "r");
        
        char* processOutput = createStringWithContentsOfFilePointer(output);
        
        JSStringRef outputString = JSStringCreateWithUTF8CString(processOutput);
        
        JSValueRef returnValue = JSValueMakeString(context, outputString);
        JSStringRelease(outputString);
        
        
        return returnValue;
    }
    return JSValueMakeUndefined(context);
}

JSValueRef JSShell_load(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    BOOL loaded = FALSE;
    if (argumentCount > 0)
    {
        JSStringRef string = JSValueToStringCopy(context, arguments[0], exception);
        size_t numChars = JSStringGetMaximumUTF8CStringSize(string);
        char stringUTF8[numChars];
        JSStringGetUTF8CString(string, stringUTF8, numChars);
        char* scriptUTF8 = createStringWithContentsOfFile(stringUTF8);
        
        if (scriptUTF8)
        {
            JSStringRef script = JSStringCreateWithUTF8CString(scriptUTF8);
            JSEvaluateScript(context, 
                             script, 
                             thisObject, 
                             NULL, 
                             1,
                             exception);
            JSStringRelease(script);
            loaded = TRUE;
        }
        
        JSStringRelease(string);
        free(scriptUTF8);
    }
    
    if (exception)
    {
        return NULL;
    }
    else if (loaded)
    {
        return JSValueMakeBoolean(context, TRUE);
    }
    else
    {
        return JSValueMakeBoolean(context, FALSE);
    }
}


JSValueRef JSShell_help(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    printf("Function Usage           Description\n"
           "======== =====           ===========\n"
           "print    print(string)   prints a line of text\n"
           "readline readline()      returns a line read from stdin\n"
           "gc       gc()            invokes the garbage collector\n"
           "quit     quit()          quits the shell\n"
           "system   system(command) runs a system command\n"
           "pread    pread(command)  runs a system command and returns stdout\n"
           "load     load(pathToJs)  load a script into the current context\n"
           "help     help()          prints this help text\n");
    return JSValueMakeUndefined(context);
}

